﻿using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Media;

namespace Creventive.Wpf.ShellFactory.Workspace.Primitives
{
    /// <summary>
    /// Presents the provided workspace item.
    /// </summary>
    public class WorkspaceItemPresenter: FrameworkElement
    {
        /// <summary>
        /// Provides a binding backing field for the WorkspaceItem property.
        /// </summary>
        public static readonly DependencyProperty WorkspaceItemProperty = DependencyProperty.Register("WorkspaceItem", typeof(WorkspaceItem), typeof(WorkspaceItemPresenter), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsMeasure, (s, e) => ((WorkspaceItemPresenter)s).OnWorkspaceItemChanged((WorkspaceItem)e.OldValue, (WorkspaceItem)e.NewValue)));

        /// <summary>
        /// Gets or sets the workspace item to display.
        /// </summary>
        /// <value>The workspace item.</value>
        public WorkspaceItem WorkspaceItem
        {
            get { return (WorkspaceItem)GetValue(WorkspaceItemProperty); }
            set { SetValue(WorkspaceItemProperty, value); }
        }

        /// <summary>
        /// Called when the WorkspaceItem property has changed.
        /// </summary>
        /// <param name="oldValue">The old value.</param>
        /// <param name="newValue">The new value.</param>
        protected virtual void OnWorkspaceItemChanged(WorkspaceItem oldValue, WorkspaceItem newValue)
        {
            if (oldValue == newValue)
                return;

            if (oldValue != null)
                this.RemoveVisualChild(oldValue);

            if (!DesignerProperties.GetIsInDesignMode(this) && newValue != null)
                this.AddVisualChild(newValue);
        }

        /// <summary>
        /// Overrides <see cref="M:System.Windows.Media.Visual.GetVisualChild(System.Int32)"/>, and returns a child at the specified index from a collection of child elements.
        /// </summary>
        /// <param name="index">The zero-based index of the requested child element in the collection.</param>
        /// <returns>
        /// The requested child element. This should not return null; if the provided index is out of range, an exception is thrown.
        /// </returns>
        protected override System.Windows.Media.Visual GetVisualChild(int index)
        {
            var item = this.WorkspaceItem;
            if (item == null)
                throw new ArgumentOutOfRangeException("index");
            else
                return item;
        }

        /// <summary>
        /// Gets the number of visual child elements within this element.
        /// </summary>
        /// <value></value>
        /// <returns>The number of visual child elements for this element.</returns>
        protected override int VisualChildrenCount
        {
            get
            {
                var item = this.WorkspaceItem;
                return (item != null) ? 1 : 0;
            }
        }

        /// <summary>
        /// When overridden in a derived class, participates in rendering operations that are directed by the layout system. The rendering instructions for this element are not used directly when this method is invoked, and are instead preserved for later asynchronous use by layout and drawing.
        /// </summary>
        /// <param name="drawingContext">The drawing instructions for a specific element. This context is provided to the layout system.</param>
        protected override void OnRender(System.Windows.Media.DrawingContext drawingContext)
        {
            if (DesignerProperties.GetIsInDesignMode(this))
                RenderPlaceholder(drawingContext, new Rect(this.RenderSize));
            base.OnRender(drawingContext);
        }

        private static void RenderPlaceholder(DrawingContext drawingContext, Rect area)
        {
            area.Offset(-0.5, -0.5);
            drawingContext.PushOpacity(0.75);
            DrawCross(drawingContext, area, new Pen(Brushes.Black, 1.0));
            DrawCross(drawingContext, area, new Pen(Brushes.White, 1.0) { DashStyle = new DashStyle(new[] { 3.0, 4.0 }, 0.0) });
            drawingContext.Pop();
        }

        private static void DrawCross(DrawingContext drawingContext, Rect area, Pen pen)
        {
            drawingContext.DrawRectangle(Brushes.Transparent, pen, area);
            drawingContext.DrawLine(pen, area.TopLeft, area.BottomRight);
            drawingContext.DrawLine(pen, area.TopRight, area.BottomLeft);
        }

        /// <summary>
        /// When overridden in a derived class, measures the size in layout required for child elements and determines a size for the <see cref="T:System.Windows.FrameworkElement"/>-derived class.
        /// </summary>
        /// <param name="availableSize">The available size that this element can give to child elements. Infinity can be specified as a value to indicate that the element will size to whatever content is available.</param>
        /// <returns>
        /// The size that this element determines it needs during layout, based on its calculations of child element sizes.
        /// </returns>
        protected override Size MeasureOverride(Size availableSize)
        {
            var item = this.WorkspaceItem;
            if (item != null)
            {
                item.Measure(availableSize);
                return item.DesiredSize;
            }
            else
                return base.MeasureOverride(availableSize);
        }

        /// <summary>
        /// When overridden in a derived class, positions child elements and determines a size for a <see cref="T:System.Windows.FrameworkElement"/> derived class.
        /// </summary>
        /// <param name="finalSize">The final area within the parent that this element should use to arrange itself and its children.</param>
        /// <returns>The actual size used.</returns>
        protected override Size ArrangeOverride(Size finalSize)
        {
            var item = this.WorkspaceItem;
            if (item != null)
            {
                item.Arrange(new Rect(finalSize));
                return item.RenderSize;
            }
            else
                return base.ArrangeOverride(finalSize);
        }
    }
}
